/*
 * Copyright (c) 2011 Ondrej Lapacek
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

/**
 * @author      Ondrej Lapacek
 * @version     1.0, 09/12/11
 * @since       1.0
 */

package cz.muni.fi.android.assistivecamera.osm;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.http.client.methods.HttpUriRequest;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;

import android.content.Context;
import android.location.Location;
import android.util.Log;
import cz.muni.fi.android.assistivecamera.R;
import cz.muni.fi.android.assistivecamera.entities.Building;
import cz.muni.fi.android.assistivecamera.osm.model.Node;
import cz.muni.fi.android.assistivecamera.osm.model.OsmData;
import cz.muni.fi.android.assistivecamera.osm.model.Primitive;
import cz.muni.fi.android.assistivecamera.texttospeech.SpeechHandler;

public class OpenStreetMap {
	private static final String TAG = "Open Street Map";
	
	private static final double DISTANCE = 0.002; //distance 222 meters
	//private static final double DISTANCE = 0.004; //distance ~ 444 meters
    public static OsmData openStreetMap(Context ctx, Location tempLocation, float azimuth, float viewAngle) {
        HttpURLConnection connection = null;
        OsmData data = null;
        List<URL> urlList = new ArrayList<URL>();
        
        float[] box = boundingBox(tempLocation, azimuth, viewAngle);
        
        try {
        	// 193.63.75.100 - www.openstreetmap.org
        	String stringURL = null;
        	
        	stringURL = ctx.getResources().getString(R.string.osm);
        	urlList.add(new URL(stringURL + box[0] + "," + box[1] + "," + box[2] + "," + box[3]));
        	Log.d(TAG, stringURL);
        	/*stringURL = ctx.getResources().getString(R.string.jxapi);
        	urlList.add(new URL(stringURL + box[0] + "," + box[1] + "," + box[2] + "," + box[3] + "]"));
        	*/
        	stringURL = ctx.getResources().getString(R.string.osmdev);
        	urlList.add(new URL(stringURL + box[0] + "," + box[1] + "," + box[2] + "," + box[3]));
        	stringURL = ctx.getResources().getString(R.string.mapquestapi);
        	urlList.add(new URL(stringURL + box[0] + "," + box[1] + "," + box[2] + "," + box[3] + "]"));
		} catch (MalformedURLException e) {
			Log.e(TAG, "Malformed URL Exception", e);
		}

        for (URL url: urlList) {
	        try {
	            
	            //set up out communications stuff
	            connection = null;
	              
	            //Set up the initial connection
	            connection = (HttpURLConnection)url.openConnection();
	            connection.setRequestMethod("GET");
	            //connection.setDoInput(true);
	            //connection.setDoOutput(true);
	            connection.setReadTimeout(10000);
	            connection.setConnectTimeout(10000);
	                          
	            connection.connect();
	            
	            try {
	            	InputStream input = connection.getInputStream();
	            	if(input == null){
	            		Log.d(TAG, "Unable to get connection to " + url);
	            	}
	                InputStreamReader isr = new InputStreamReader(input);
	                InputSource source = new InputSource(isr);
	                SAXParserFactory factory = SAXParserFactory.newInstance();
	                SAXParser parser = factory.newSAXParser();
	                XMLReader xr = parser.getXMLReader();
	                OpenStreetMapXmlHandler handler = new OpenStreetMapXmlHandler();
	                
	                xr.setContentHandler(handler);
	                xr.parse(source);
	                
	                data = handler.getData();
	                
	                break;
	                
	            } catch (Exception e) {
	            	Log.e(TAG, "Exception", e);
	            	continue;
	            }
	        } catch (Exception e) {
	        	Log.w(TAG, "Exception", e);

	        	if (urlList.indexOf(url) == urlList.size() - 1) 
	        		SpeechHandler.ttsQueue(ctx.getResources().getString(R.string.internetConn), 0, 1);
	        	
	        	continue;
	        }
        }
        
        return data;
    }
    
    private static float[] boundingBox(Location tempLocation, float azimuth, float viewAngle) {
    	float[] triangle = new float[6];
        
        float firstAngle = azimuth + (viewAngle/2);
        float bottomAngle = azimuth - (viewAngle/2);
        
        triangle[0] = (float) tempLocation.getLongitude();
        triangle[1] = (float) tempLocation.getLatitude();
        
        triangle[2] = (float) (tempLocation.getLongitude() + DISTANCE * Math.sin(firstAngle * Math.PI / 180));
        triangle[3] = (float) (tempLocation.getLatitude() + DISTANCE * Math.cos(firstAngle * Math.PI / 180));
        
        triangle[4] = (float) (tempLocation.getLongitude() + DISTANCE * Math.sin(bottomAngle * Math.PI / 180));
        triangle[5] = (float) (tempLocation.getLatitude() + DISTANCE * Math.cos(bottomAngle * Math.PI / 180));
		
		float[] box = {triangle[0], triangle[1], triangle[0], triangle[1]};
		
		for (int i = 0; i < 6; i += 2) {
			if (box[0] > triangle[i]) box[0] = triangle[i];
			if (box[2] < triangle[i]) box[2] = triangle[i];
		}
		
		for (int i = 1; i < 6; i += 2) {
			if (box[1] > triangle[i]) box[1] = triangle[i];
			if (box[3] < triangle[i]) box[3] = triangle[i];
		}
		
		return box;
    }
    
    /**
     * Returns location of building with particular name within range 222 meters from the given 
     * location
     * @param ctx application context
     * @param buildingName name of searched building
     * @param location current location 
     * @return building info (name, longitude, latitude) if building is found, null otherwise.
     */
    
    public static Building getBuildingInfo(Context ctx, String buildingName, Location location){
    	Building result = null;
    	OsmData data = null;
    	
    	for(int azimuth = 0 ; azimuth<360 ; azimuth += 90){
    		data = OpenStreetMap.openStreetMap(ctx, location, azimuth, 90);
    		Node node = searchDataTags(buildingName,data);
            if(node != null){
            	result = new Building(buildingName);
            	result.setLocation(node.getLocation());
            	return result;
            }else{
            	Log.d(TAG,"Building" + buildingName + " not found.");
            }
    	}
    	
    	return result;
    	
    
    }

	private static Node searchDataTags(String buildingName, OsmData data) {
		if(data == null){
			Log.d(TAG,"Parametr data je null:" + data);
			return null;
		}
		Map<Long,Node> nodes = data.getNodes();
		if(nodes !=null){
			for(Node node:nodes.values()){
				if(node.getTags().containsValue(buildingName)){
					return node;
				}
				Map<String, String> tags = node.getTags();
				if(tags == null){
					return null;
				}
				for(String tag:tags.values()){
					Log.i(TAG,"Tag: " + tag);
					if(tag.compareToIgnoreCase(buildingName)==0){
						return node;
					}
				}
			}
		}else{
			Log.d(TAG,"No nodes in data from openstreetmap: "+nodes);
			
		}
		return null;
	}
}
